/**
 * BudgetBuoy
 * 
 * Simplify your personal expense management
 * 
 * @team cs2103jan13-w15-3j
 */

//--------------------------------------------------------------

/**
 * API Class
 * 
 * Description:
 * 
 * 
 * @package logic.api
 * @author  Chan Jan Lin (A0081246J)
 * @editor(s) -
 */



//Get rid of Substring(2) when its empty when writing loans
package logic;

import java.io.*;
import java.util.*;
//import au.com.bytecode.opencsv;

import shared.*;

public class API {
	public static final String CashLoan = "Loan Repayment";
	
	private File recordFile = new File("record.csv");
	private File initFile = new File("init.txt");
	private File loanFile = new File("loan.csv");
	private File categoryFile = new File("category.csv");
	private File modeFile = new File("mode.csv");
	private File receiptFile = new File("receipt.csv");
	public TreeSet<Record> dateTree, nameTree;
	private ArrayList<ReceiptItem> itemList;
	private TreeMap<Integer, Income> incomeTree;
	private TreeMap<Integer, Expense> expenseTree;
	private TreeMap<Integer, Loan> loanTree;
	//private TreeMap<ReceiptItem, Receipt> modelTree, serialTree, brandTree;
	private TreeSet<Receipt> shopTree;
	private int idCounter = 1, accountBalance, totalOwed, minBalance, daysToPayment;
	private ArrayList<Category> categoryList;
	private ArrayList<Mode> modeList;
	
	public API(){
		/*
		dateTree = new TreeSet<Record>(new CompareDate());
		nameTree = new TreeSet<Record>(new CompareName());
		incomeTree = new TreeMap<Integer, Income>();
		expenseTree = new TreeMap<Integer, Expense>();
		loanTree = new TreeMap<Integer, Loan>();
		categoryList = new ArrayList<Category>();
		modeList = new ArrayList<Mode>();
		
		categoryList.add(new Category("Uncategorised"));
		categoryList.add(new Category(CashLoan));
		modeList.add(new Mode("Uncategorised"));
		*/
		reset();
		minBalance = 10000;
		daysToPayment = 3;
	}
	
	public void reset(){
		dateTree = new TreeSet<Record>(new CompareDate());
		nameTree = new TreeSet<Record>(new CompareName());
		shopTree = new TreeSet<Receipt>(new CompareShop());
		incomeTree = new TreeMap<Integer, Income>();
		expenseTree = new TreeMap<Integer, Expense>();
		loanTree = new TreeMap<Integer, Loan>();
		categoryList = new ArrayList<Category>();
		modeList = new ArrayList<Mode>();
		itemList = new ArrayList<ReceiptItem>();
		
		categoryList.add(new Category("Uncategorised"));
		categoryList.add(new Category(CashLoan));
		modeList.add(new Mode("Uncategorised"));
		idCounter=1;
		accountBalance = 0;
		totalOwed = 0;
	}
	
	public int getBalance(){return accountBalance;}
	public int getOutstanding(){return totalOwed;}
	public ArrayList<Loan> getLoanList(){	return new ArrayList<Loan>(loanTree.values());	}
	public ArrayList<Category> getCategoryList(){	return new ArrayList<Category>(categoryList);	}
	public ArrayList<Mode> getModeList(){	return new ArrayList<Mode>(modeList);	}
	
	public int getDaysToPayment(){
		return daysToPayment;
	}
	
	public void setDaysToPayment(int daysToPayment){
		this.daysToPayment = daysToPayment;
	}
	
	public int getMinBalance(){
		return minBalance;
	}
	
	public void setMinBalance(int minBalance){
		this.minBalance = minBalance;
	}
	
	private class CompareName implements Comparator<Record>{

		@Override
		public int compare(Record record1, Record record2) {
			if(record1.getName().equals(record2.getName()))
				return record1.getID() - record2.getID();
			return record1.getName().compareTo(record2.getName());
		}
	}
	private class CompareDate implements Comparator<Record>{

		@Override
		public int compare(Record record1, Record record2) {
			if(record1.getYear()!=record2.getYear())
				return record1.getYear() - record2.getYear();
			if(record1.getMonth()!=record2.getMonth())
				return record1.getMonth() - record2.getMonth();
			if(record1.getDay()!=record2.getDay())
				return record1.getDay() - record2.getDay();
			return record1.getID() - record2.getID();
		}
	}
	
	private class CompareShop implements Comparator<Receipt>{

		@Override
		public int compare(Receipt first, Receipt second){
			if(first.getSeller().equals(second.getSeller()))
				return first.getID() - second.getID();
			return first.getSeller().compareTo(second.getSeller());
		}
		
	}
	
	public void init() throws Exception{
		readInit();
		readCategory();
		readMode();
		readRecord();
		readLoan();
		readReceipt();
		//save();	//If implement save at every change;
	}

	private void readInit() throws Exception {
		if(initFile.exists()){
			Scanner scanner = new Scanner(initFile);
			idCounter = scanner.nextInt();
			minBalance = scanner.nextInt();
			daysToPayment = scanner.nextInt();
			return;
		}
		//Add as default
		//alert = new Alert(10000,3);
	}
	
	//prerequisite: input uses NO delimiters
	private String nextString(Scanner input) throws Exception{
		input.skip("\\s*\\\"");
		String output = input.findInLine("[^\\\"]*(\\\"\\\"[^\\\"]*)*");
		//System.out.println(output);
		input.skip("\\\"\\s*,?");
		
		return output.replace("\"\"", "\"");
		/*
		String answer = "";
		
		input.useDelimiter("");
		System.out.println(input.next("\\s+"));
		//while(a!='"')
			//System.out.println(a+a=='"');
		input.next("\\\"");
		String next = input.next(".");
		while(true){
			if(next.equals("\"")){
				if(!input.hasNext("."))
					return answer;
				String following = input.next(".");
				if(following.equals(",")){
					input.useDelimiter("\\s");
					return answer;
				}
				else if(following.equals("\""))
					answer = answer+'"';
				else
					throw new Exception("Record file of wrong CSV format");
			}
			answer = answer+next;
			System.out.println(answer);
			next=input.next(".");
		}*/
	}

	//prerequisite: input uses NO delimiters
	//TOKEEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	private int nextInt(Scanner input) throws Exception{
		//System.out.println(input.next());
		//String output = input.findInLine("\\-?\\d+");
		String output = input.findInLine("\\-?\\d+");
		input.skip("\\s*,?");
		
		return Integer.valueOf(output);
	}
	
	private void readCategory() throws Exception{
		if(categoryFile.exists()){
			Scanner scanner = new Scanner(new FileReader(categoryFile));
			while(scanner.hasNextLine())
				categoryList.add(new Category(scanner.nextLine()));
		}else{
			//Added by Default
			categoryList.add(new Category("Food"));
			categoryList.add(new Category("Entertainment"));
			categoryList.add(new Category("Utilities"));
			categoryList.add(new Category("Clothes"));
			
		}
		return;
	}
	
	private void readMode() throws Exception{
		if(modeFile.exists()){
			Scanner scanner = new Scanner(new FileReader(modeFile));
			while(scanner.hasNextLine())
				modeList.add(new Mode(scanner.nextLine()));
		}else{
			//Added by Default
			modeList.add(new Mode("Cash"));
			modeList.add(new Mode("NETS"));
			modeList.add(new Mode("GIRO"));
			modeList.add(new Mode("Loan"));
			
		}
		return;
	}
	
	
	
	private void readRecord() throws Exception{
		int id, amount, day, month, year, mode, category;
		String name, description;
		
		incomeTree = new TreeMap<Integer, Income>();
		expenseTree = new TreeMap<Integer, Expense>();
		nameTree = new TreeSet<Record>(new CompareName());
		dateTree = new TreeSet<Record>(new CompareDate());
		
		if(recordFile.exists()){
			FileReader records = new FileReader(recordFile);
			Scanner scanner = new Scanner(records);
			while(scanner.hasNextLine()){
				String string = scanner.nextLine();
				//System.out.println(string);
				Scanner line = new Scanner(string);
				
				id = nextInt(line);
				name = nextString(line);
				amount = nextInt(line);
				day = nextInt(line);
				month = nextInt(line);
				year = nextInt(line);
				mode = nextInt(line);
				category = nextInt(line);
				description = nextString(line);
				
				/*
				id = line.nextInt();
				line.next();
				name = nextString(line);
				amount = line.nextInt();
				line.next();
				day = line.nextInt();
				line.next();
				month = line.nextInt();
				line.next();
				year = line.nextInt();
				line.next();
				mode = line.nextInt();
				line.next();
				category = line.nextInt();
				line.next();
				description = nextString(line);
				*/
				if(mode<0){//Income
					Income income = new Income(id, name, amount, day, month, year, description);
					incomeTree.put(income.getID(), income);
					dateTree.add(income);
					nameTree.add(income);
					accountBalance += amount;
				}else{
					Expense expense = new Expense(id, name, amount, day, month, year, description,
							modeList.get(mode), categoryList.get(category));
					expenseTree.put(expense.getID(), expense);
					dateTree.add(expense);
					nameTree.add(expense);
					accountBalance -= amount;
				}
			}
		}
		return;
		
	}
	
	private void readLoan() throws Exception{
		if(loanFile.exists()){
			int id, amount, day, month, year, status, category, recordID;
			Record record; //the original object bought with the loan
			Loan loan;
			String name, lender, description;
			FileReader loans = new FileReader(loanFile);
			Scanner scanner = new Scanner(loans);
			while(scanner.hasNextLine()){
				Scanner line = new Scanner(scanner.nextLine());

				id = nextInt(line);
				name = nextString(line);
				amount = nextInt(line);
				day = nextInt(line);
				month = nextInt(line);
				year = nextInt(line);
				status = nextInt(line);
				category = nextInt(line);
				recordID = nextInt(line);
				lender = nextString(line);
				description = nextString(line);
				if(category>=categoryList.size())
					throw new Exception("LoanFile: CategoryIndex out of bound");
				if(category==1)
					record = new Expense(-1, name, recordID, day, month, day, null, null, null);
				else
					record = incomeTree.get(recordID);
				if(record == null)
					throw new Exception("LoanFile: Corrupted Values of Income ID");
				loan = new Loan(id, name, amount, day, month, year, description, categoryList.get(category), lender, record);
				loanTree.put(id, loan);
				nameTree.add(loan);
			
				String expenseString = scanner.nextLine();
				for(String stringID:expenseString.split(",")){
					if(stringID.matches("\\s*"))
						break;
					if(!expenseTree.containsKey(Integer.valueOf(stringID.replaceAll("\\s*", ""))))
						throw new Exception("LoanFile: Corrupted Values of Installments ID");
					loan.addInstallment(expenseTree.get(Integer.valueOf(stringID.replaceAll("\\s*", ""))));
					//to implement reverse link from expense to loan
				}

				String[] paymentString = scanner.nextLine().replaceAll("\\s*", "").split(",");
				for(int i=0;i<paymentString.length;i+=4){
					if(paymentString[0].matches("\\s*"))
						break;
					loan.getFuturePayments().add(new Payment(Integer.valueOf(paymentString[i]), 
							Integer.valueOf(paymentString[i+1]), Integer.valueOf(paymentString[i+2]),
							Integer.valueOf(paymentString[i+3])));
				}
				
				if(status==0)
					loan.clear();
				else
					totalOwed += loan.getOutstanding();
			}
		}
		return;
	}
	
	private void readReceipt() throws Exception{
		if(receiptFile.exists()){
			int id, amount, day, month, year, mode, category;
			String name, description, receiptNo, shop, contact;
			
			FileReader receipts = new FileReader(receiptFile);
			Scanner scanner = new Scanner(receipts);

			while(scanner.hasNextLine()){
				Scanner line = new Scanner(scanner.nextLine());
				id = nextInt(line);
				name = nextString(line);
				amount = nextInt(line);
				day = nextInt(line);
				month = nextInt(line);
				year = nextInt(line);
				mode = nextInt(line);
				category = nextInt(line);
				description = nextString(line);
				receiptNo = nextString(line);
				shop = nextString(line);
				contact = nextString(line);
				//ArrayList<ReceiptItem> itemList = new ArrayList<ReceiptItem>();
				Receipt receipt = new Receipt(id, name, amount, day, month, year, description, modeList.get(mode),
						categoryList.get(category), shop, receiptNo, contact);
				addReceipt(receipt);
				
				String nextLine;
				while(!(nextLine = scanner.nextLine()).isEmpty()){
					String itemName, serial, model, brand, itemDescription;
					int itemAmount;
					
					line = new Scanner(nextLine);
					itemName = nextString(line);
					itemAmount = nextInt(line);
					model = nextString(line);
					serial = nextString(line);
					brand = nextString(line);
					itemDescription = nextString(line);
					ReceiptItem item = new ReceiptItem(itemName, itemAmount, serial, model, brand, itemDescription, receipt);
					addReceiptItem(receipt, item);
				}
			}
		}
		return;
	}
	
	public void save() throws Exception{
		writeInit();
		writeCategory();
		writeMode();
		writeRecord();
		writeLoan();
	}
	
	private void writeInit() throws Exception{
		//File temp = new File("initTemp.txt");
		PrintStream out = new PrintStream(initFile);
		out.println(idCounter);
		out.println(minBalance);
		out.println(daysToPayment);
		
		//System.out.println(initFile.getPath() + initFile.getAbsolutePath());
		//if(!temp.renameTo(initFile.getAbsoluteFile()))
			//throw new Exception("Init not renamed");
		return;
	}
	
	private void writeCategory() throws Exception{
		//File temp = new File("categoryTemp.csv");
		PrintStream out = new PrintStream(categoryFile);
		
		for(int i = 2;i<categoryList.size();i++)
			out.println(categoryList.get(i));
		
		//if(!temp.renameTo(categoryFile))
			//throw new Exception("Category not renamed");
		return;
	}
	
	private void writeMode() throws Exception{
		//File temp = new File("modeTemp.csv");
		PrintStream out = new PrintStream(modeFile);
		
		for(int i = 1;i<modeList.size();i++)
			out.println(modeList.get(i));

		//temp.renameTo(modeFile);
		return;
	}
	
	private void writeRecord() throws Exception{
		//File temp = new File("recordTemp.csv");
		PrintStream out = new PrintStream(recordFile);
		PrintStream receipt = new PrintStream(receiptFile);
		/*for(Record current:incomeTree.values()){
			int category = -1, mode = -1;
		
			if(current.getClass() == Expense.class){
				category = categoryList.indexOf(((Expense)current).getCat());
				mode = modeList.indexOf(((Expense)current).getMode());
			}
			out.println(current.getID()+", \""+current.getName()+"\", "+current.getAmt()+", "+current.getDay()
					+", "+current.getMonth()+", "+current.getYear()+", "+mode+", "+category+", \""
					+current.getDetail()+"\"");
		}*/
		for(Income current:incomeTree.values()){
			out.println(current.getID()+" , \""+current.getName().replaceAll("\\\"", "\"\"")+"\", "+current.getAmt()+" , "+current.getDay()
					+" , "+current.getMonth()+" , "+current.getYear()+" , -1 , -1 , \""
					+current.getDetail().replaceAll("\\\"", "\"\"")+"\"");
		}
		for(Expense current:expenseTree.values()){
			if(current.getClass() == Receipt.class){
				receipt.println(current.getID()+" , \""+current.getName().replaceAll("\\\"", "\"\"")+"\", "+current.getAmt()+" , "+current.getDay()
						+" , "+current.getMonth()+" , "+current.getYear()+" , "+modeList.indexOf(current.getMode())
						+" , "+categoryList.indexOf(current.getCat())+" , \""+current.getDetail().replaceAll("\\\"", "\"\"")
						+"\" , \""+((Receipt) current).getReceiptNO().replaceAll("\\\"", "\"\"")
						+"\" , \""+((Receipt) current).getSeller().replaceAll("\\\"", "\"\"")
						+"\" , \""+((Receipt) current).getContact().replaceAll("\\\"", "\"\"")+"\"");
				for(ReceiptItem item:((Receipt) current).getItems())
					receipt.println("\""+item.getName().replaceAll("\\\"", "\"\"")+"\", "+item.getAmt()
							+" , \""+item.getModel().replaceAll("\\\"", "\"\"")
							+"\" , \""+item.getSerial().replaceAll("\\\"", "\"\"")
							+"\" , \""+item.getBrand().replaceAll("\\\"", "\"\"")
							+"\" , \""+item.getDetails().replaceAll("\\\"", "\"\"")	+"\"");
				receipt.println();
			}
			else
				out.println(current.getID()+" , \""+current.getName().replaceAll("\\\"", "\"\"")+"\", "+current.getAmt()+" , "+current.getDay()
						+" , "+current.getMonth()+" , "+current.getYear()+" , "+modeList.indexOf(current.getMode())
						+" , "+categoryList.indexOf(current.getCat())+" , \""+current.getDetail().replaceAll("\\\"", "\"\"")+"\"");
		}
		//temp.renameTo(recordFile);
		return;
	}
	
	private void writeLoan() throws Exception{
		//File temp = new File("loanTemp.csv");
		PrintStream out = new PrintStream(loanFile);
		for(Loan current:loanTree.values()){//implement for loop here for iterable
			out.println(current.getID()+" , \""+current.getName().replaceAll("\\\"", "\"\"")+"\", "+current.getAmt()+" , "+current.getDay()
					+" , "+current.getMonth()+" , "+current.getYear()+" , "+categoryList.indexOf(current.getCat())
					+" , "+(current.isClear()?0:1)+" , "+current.getInitial().getID()
					+" , \""+current.getLender().replaceAll("\\\"", "\"\"")
					+"\", \""+current.getDetail().replaceAll("\\\"", "\"\"")+"\"");
			
			String print = "";
			
			for(Expense installment:current.getInstallments())
				print = print + ", " + installment.getID();
			if(print.length()>=2)
				out.println(print.substring(2));
			else
				out.println();
			
			print = "";
			for(Payment payment:current.getFuturePayments())
				print = print + ", " + payment.getAmt() + ", " + payment.getDay() + ", " + payment.getMonth()
						 + ", " + payment.getYear();
			if(print.length()>=2)
				out.println(print.substring(2));
			else
				out.println();
		}
		//temp.renameTo(loanFile);
		return;
	}
	
	public void modifyName(Record record, String name) throws Exception{
		if(record == null)
			throw new Exception("Modifying Null record name");
		
		if(record.getClass()!=Loan.class)
			nameTree.remove(record);
		record.modifyName(name);
		if(record.getClass()!=Loan.class)
			nameTree.add(record);
		return;
	}
	
	public void modifyDate(Record record, int day, int month, int year) throws Exception{
		if(record == null)
			throw new Exception("Modifying Null record date");
		
		if(record.getClass()!=Loan.class)
			dateTree.remove(record);
		record.modifyDay(day);
		record.modifyMonth(month);
		record.modifyYear(year);
		if(record.getClass()!=Loan.class)
			dateTree.add(record);
		return;
	}
	//.....
	//.....
	//
	//
	public void modifyDescription(Record record, String desc){	record.modifyDetail(desc); return;}
	
	public Income addIncome(String name, int amount, int day, int month, int year, String description)
			throws Exception{
		Income income = new Income(idCounter, name, amount, day, month, year, description);
		incomeTree.put(idCounter,income);
		dateTree.add(income);
		nameTree.add(income);
		accountBalance += amount;
		idCounter++;//Problem for undo?
		
		return income;
	}
	
	public void addIncome(Income income) throws Exception{	
		if(income == null)
			throw new Exception("Adding Null income");
		
		incomeTree.put(income.getID(), income);
		nameTree.add(income);
		dateTree.add(income);
		accountBalance += income.getAmt();
	}
	
	public void deleteIncome(Income income) throws Exception{
		if(income == null)
			throw new Exception("Deleting Null income");
		
		incomeTree.remove(income.getID());
		dateTree.remove(income);
		nameTree.remove(income);
		accountBalance -= income.getAmt();
		return;
	}
	
	public void modifyIncomeName(Income income, String name) throws Exception{
		if(income == null)
			throw new Exception("Modifying Null income name");
		if(name == null)
			throw new Exception("Modifying income with null name");
		
		
		nameTree.remove(income);
		income.modifyName(name);
		nameTree.add(income);
	}
	
	public void modifyIncomeAmount(Income income, int amount) throws Exception{
		if(income == null)
			throw new Exception("Modifying Null income amount");
		
		accountBalance -= income.getAmt();
		income.modifyAmt(amount);
		accountBalance += amount;
		return;
	}
	
	public void modifyIncomeDate(Income income, int day, int month, int year) throws Exception{
		if(income == null)
			throw new Exception("Modifying Null income date");
		
		dateTree.remove(income);
		income.modifyDay(day);
		income.modifyMonth(month);
		income.modifyYear(year);
		dateTree.add(income);
	}
	
	public Expense addExpense(String name, int amount, int day, int month, int year, String description,
			Mode mode, Category category) throws Exception{
		Expense expense = new Expense(idCounter, name, amount, day, month, year, description, mode, category);
		expenseTree.put(idCounter, expense);
		dateTree.add(expense);
		nameTree.add(expense);
		accountBalance -= amount;
		idCounter++;//Problem?
		
		return expense;
	}
	
	public void addExpense(Expense expense) throws Exception{
		if(expense == null)
			throw new Exception("Adding Null expense");
		
		expenseTree.put(expense.getID(), expense);
		dateTree.add(expense);
		nameTree.add(expense);
		accountBalance -= expense.getAmt();
	}
	
	public void deleteExpense(Expense expense) throws Exception{
		if(expense == null)
			throw new Exception("Deleting Null expense");
		
		expenseTree.remove(expense.getID());
		dateTree.remove(expense);
		nameTree.remove(expense);
		accountBalance += expense.getAmt();
		if(expense.getLinkToLoan()!=null)
			expense.getLinkToLoan().getInstallments().remove(expense);
		return;
	}

	public void modifyExpenseName(Expense expense, String name) throws Exception{
		if(expense == null)
			throw new Exception("Modifying Null expense name");
		if(name == null)
			throw new Exception("Modifying expense with Null name");
		
		nameTree.remove(expense);
		expense.modifyName(name);
		nameTree.add(expense);
	}
	
	public void modifyExpenseAmount(Expense expense, int amount) throws Exception{
		if(expense == null)
			throw new Exception("Modifying Null expense amount");
		
		accountBalance += expense.getAmt();
		expense.modifyAmt(amount);
		accountBalance -= amount;
		return;
	}
	
	//## Jan    (changed by Si Ling)
	//##  parameter should be Expense expense instead of Income expense 
	public void modifyExpenseDate(Expense expense, int day, int month, int year) throws Exception{
		if(expense == null)
			throw new Exception("Modifying Null expense date");
		
		dateTree.remove(expense);
		expense.modifyDay(day);
		expense.modifyMonth(month);
		expense.modifyYear(year);
		dateTree.add(expense);
	}
	
	public Loan addCashLoan(String name, int amount, int day, int month, int year, String description,
			int initial, String lender) throws Exception{
		Income income = addIncome("Loan: "+name, initial, day, month, year, description);
		Loan loan = new Loan(idCounter, name, amount, day, month, year, description, categoryList.get(1), lender, income);
		loanTree.put(idCounter, loan);
		nameTree.add(loan);
		totalOwed += loan.getOutstanding();
		
		idCounter++;
		return loan;
	}
	
	public Loan addItemLoan(String name, int amount, int day, int month, int year, String description,
			int initial, Category category, String lender) throws Exception{
		Expense expense = new Expense(-1, name, initial, day, month, year, null, null, null);
		Loan loan = new Loan(idCounter, name, amount, day, month, year, description, category, lender, expense);
		loanTree.put(idCounter, loan);
		nameTree.add(loan);
		totalOwed += loan.getOutstanding();

		idCounter++;
		return loan;
	}
	
	public void addLoan(Loan loan) throws Exception{
		if(loan == null)
			throw new Exception("Adding Null loan");
		
		loanTree.put(loan.getID(), loan);
		nameTree.add(loan);
		for(Expense install:loan.getInstallments())
			addExpense(install);
		totalOwed += loan.getOutstanding();
		
	}
	
	public Expense addLoanInstallment(String name, int amount, int day, int month, int year, String description,
			Mode mode, Loan loan) throws Exception{
		Expense installment = addExpense(name, amount, day, month, year, description, mode, loan.getCat());
		loan.addInstallment(installment);
		return installment;
	}
	
	public void deleteLoan(Loan loan) throws Exception{
		if(loan == null)
			throw new Exception("Deleting Null loan");
		
		for(Expense current:loan.getInstallments()){
			incomeTree.remove(current);
			nameTree.remove(current);
			dateTree.remove(current);
			accountBalance += current.getAmt();
		}if(loan.getInitial().getClass() == Income.class)
			deleteIncome((Income)loan.getInitial());
		loanTree.remove(loan.getID());
		nameTree.remove(loan);
		if(!loan.isClear())
			totalOwed -= loan.getOutstanding();
		
		return;
	}
	
	public void modifyLoanAmount(Loan loan, int amount) throws Exception{
		if(loan == null)
			throw new Exception("Modifying Null loan amount");
		
		if(!loan.isClear())
			totalOwed -= loan.getOutstanding();
		loan.modifyAmt(amount);
		if(!loan.isClear())
			totalOwed += loan.getOutstanding();
		return;
	}
	
	public void modifyLoanInitial(Loan loan, int amount) throws Exception{
		if(loan == null)
			throw new Exception("Modifying Null loan initial amount");
		
		if(loan.getInitial().getClass()==Income.class)
			modifyIncomeAmount((Income)loan.getInitial(), amount);
		else
			loan.modifyIniAmt(amount);
	}
	
	public void clearLoan(Loan loan) throws Exception{
		if(loan == null)
			throw new Exception("Clearing Null loan");
		
		loan.clear();
		totalOwed -= loan.getOutstanding();
		return;
	}
	
	public void unclearLoan(Loan loan) throws Exception{
		if(loan == null)
			throw new Exception("Unclearing Null loan");
		
		loan.unclear();
		totalOwed += loan.getOutstanding();
		return;
	}

	public Receipt addReceipt(String name, int amount, int day, int month, int year,
			String description, Mode mode, Category category, String shop, 
			String receiptNO, String contact) throws Exception {
		Receipt receipt = new Receipt(idCounter, name, amount, day, month, year, description, mode, category, 
				shop, receiptNO, contact);
		addReceipt(receipt);
		idCounter++;
		return receipt;
	}
	
	public void addReceipt(Receipt receipt) throws Exception{
		if(receipt == null)
			throw new Exception("Adding Null receipt");
		expenseTree.put(receipt.getID(), receipt);
		nameTree.add(receipt);
		dateTree.add(receipt);
		shopTree.add(receipt);
		for(ReceiptItem item:receipt.getItems())
			itemList.add(item);
		accountBalance -= receipt.getAmt();
	}
	
	public void addReceiptItem(Receipt receipt, ReceiptItem item) {
		itemList.add(item);
		receipt.addItem(item);
	}
	
	public void deleteReceiptItem(Receipt receipt, ReceiptItem item){
		itemList.remove(item);
		receipt.deleteItem(item);
	}

	public void delete(Receipt receipt) throws Exception{
		if(receipt == null)
			throw new Exception("Deleting Null receipt");
		expenseTree.remove(receipt.getID());
		nameTree.remove(receipt);
		dateTree.remove(receipt);
		shopTree.remove(receipt);
		for(ReceiptItem item:receipt.getItems())
			itemList.remove(item);
		accountBalance += receipt.getAmt();
	}
	
	public ArrayList<ReceiptItem> getBrandRange(String name){
		ArrayList<ReceiptItem> results = new ArrayList<ReceiptItem>();
		
		for(ReceiptItem item:itemList)
			if(item.getBrand().startsWith(name))
				results.add(item);
		
		return results;
	}
	
	public ArrayList<ReceiptItem> getItemNameRange(String name){
		ArrayList<ReceiptItem> results = new ArrayList<ReceiptItem>();
		
		for(ReceiptItem item:itemList)
			if(item.getName().startsWith(name))
				results.add(item);
		
		return results;
	}
	
	public ArrayList<ReceiptItem> getModelRange(String name){
		ArrayList<ReceiptItem> results = new ArrayList<ReceiptItem>();
		
		for(ReceiptItem item:itemList)
			if(item.getModel().startsWith(name))
				results.add(item);
		
		//TODO SORT
		return results;
	}
	
	public ArrayList<ReceiptItem> getSerialNumRange(String name){
		ArrayList<ReceiptItem> results = new ArrayList<ReceiptItem>();
		
		for(ReceiptItem item:itemList)
			if(item.getSerial().startsWith(name))
				results.add(item);
			
		return results;
	}
	
	public Record[] getDateArray(){
		return dateTree.descendingSet().toArray(new Record[0]);
	}
	
	public SortedSet<Record> getDateRange(int startDay, int startMonth, int startYear, int endDay, int endMonth, int endYear){
		
		return dateTree.subSet(new Income(0, null, 0, startDay, startMonth, startYear, null),
				new Income(0, null, 0, endDay, endMonth, endYear, null));
	}
	
	public SortedSet<Record> getNameRange(String name) throws Exception{
		if(name == null)
			throw new Exception("Searching name with null String");
		int length = name.length();
		if(length==0)
			return nameTree.tailSet(new Income(0, "", 0, 0, 0, 0, null));
		
		//[For debugging] System.out.println("Got: "+name+name.substring(0, length-1)+(name.charAt(length-1)+1));
		return nameTree.subSet(new Income(0, name, 0, 0, 0, 0, null),
				new Income(0, name.substring(0, length-1)+(char)(name.charAt(length-1)+1), 0, 0, 0, 0, null));
	}
	
	public Mode addMode(String name) throws Exception{
		if(name == null)
			throw new Exception("Adding payment mode with null String");
		for(Mode current:modeList)
			if(current.getName().equals(name))
				throw new Exception("Payment mode name already exists!");
		
		Mode mode = new Mode(name);
		modeList.add(mode);
		return mode;
	}	
	
	public void modifyMode(Mode mode, String name) throws Exception{
		if(name == null)
			throw new Exception("Modify mode with null String");
		if(mode == null)
			throw new Exception("Modify payment mode with null Mode");
		for(Mode current:modeList)
			if(current.getName().equals(name))
				throw new Exception("Payment mode name already exists!");

		mode.modifyName(name);
		
	}
	
	public ArrayList<Record> mergeMode(Mode from, Mode to) throws Exception{
		if(from==null || to==null)
			throw new Exception("Merging categories with null");
		if(from==modeList.get(0))
			throw new Exception("Cannot merge Uncategorised category");
		
		ArrayList<Record> list = new ArrayList<Record>();
		for(Expense expense:expenseTree.values())
			if(expense.getMode() == from){
				expense.modifyMode(to);
				list.add(expense);
			}
		modeList.remove(from);
		return list;
	}
	
	public Category addCategory(String name) throws Exception{
		if(name == null)
			throw new Exception("Adding category with null String");
		for(Category current:categoryList)
			if(current.getName().equals(name))
				throw new Exception("Category name already exists!");
		
		Category category = new Category(name);
		categoryList.add(category);
		return category;
	}
	
	public void modifyCategory(Category category, String name) throws Exception{
		if(name == null)
			throw new Exception("Modify category with null String");
		if(category == null)
			throw new Exception("Modify category with null Category");
		for(Category current:categoryList)
			if(current.getName().equals(name))
				throw new Exception("Category name already exists!");

		category.modifyName(name);
		
	}
	
	public ArrayList<Record> mergeCategory(Category from, Category to) throws Exception{
		if(from==null || to==null)
			throw new Exception("Merging categories with null");
		if(from==categoryList.get(0))
			throw new Exception("Cannot merge Uncategorised category");
		if(from==categoryList.get(1))
			throw new Exception("Cannot merge Loan repayment category");
		if(to==categoryList.get(1))
			throw new Exception("Cannot merge into Loan repayment category");
		
		ArrayList<Record> list = new ArrayList<Record>();
		for(Expense expense:expenseTree.values())
			if(expense.getCat() == from){
				expense.modifyCat(to);
				list.add(expense);
			}
		for(Loan loan:loanTree.values())
			if(loan.getCat()==from){
				loan.modifyCategory(to);
				list.add(loan);
			}
		categoryList.remove(from);
		return list;
	}
	
	private void test() throws Exception{
		init();
		setDaysToPayment(100);
		setMinBalance(50000);
		Income income = addIncome("Balance Brought Forward", 100000, 1, 1, 2013, "From, Previous\\\" Balance.");
		if(nameTree.contains(income)){
			System.out.println("Found");
		}
		deleteIncome(income);
		if(!nameTree.contains(income))
			System.out.println("Gone");
		
		save();
	
	}
	
	public static void main(String[] args){
		API api = new API();
		try {
			api.init();
			api.save();
			for(Object object: api.getNameRange("Receipt")){
				System.out.println(object);
			}
			
			//Excel.load(api);
			//Excel.export(api);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
